# -*- coding: utf-8 -*-
"""
VBAT监控模块
功能：支持电压监控、阈值检查、数据记录、MQTT告警和配置管理
"""

import json
import csv
from datetime import datetime
import re
import time
from typing import List, Dict, Any, Optional

# 导入配置管理器
from src.managers.config_manager import config_manager

class VBATMonitor:
    """VBAT监控类"""
    def __init__(self):
        # VBAT监控配置
        self.vbat_monitor_enabled = False  # VBAT监控开关
        self.vbat_threshold_low = 3300     # 低阈值(mV)
        self.vbat_threshold_high = 4500    # 高阈值(mV)
        self.vbat_max_records = 50         # 最大记录数
        self.vbat_records = []             # VBAT记录列表
        self.vbat_mqtt_enabled = False     # MQTT发送开关
        self.vbat_mqtt_command = ""        # MQTT连接AT指令(已废弃)
        self.vbat_mqtt_topic = None         # MQTT主题
        self.vbat_mqtt_broker = None        # MQTT服务器
        self.vbat_mqtt_port = None          # MQTT端口
        self.vbat_mqtt_client_id = "vbat_monitor_001"  # 客户端ID
        self.vbat_mqtt_username = ""        # 用户名
        self.vbat_mqtt_password = ""        # 密码
        self.vbat_mqtt_qos = None           # QoS等级
        self.vbat_mqtt_connect_cmd = "AT+ECMTOPEN=0,{broker},{port}"  # 连接指令模板
        self.vbat_mqtt_subscribe_cmd = "AT+ECMTSUB=0,{topic},{qos}"   # 订阅指令模板
        self.vbat_mqtt_publish_cmd = "AT+ECMTPUB=0,{topic},{qos},{json}"  # 发布指令模板
        self.vbat_mqtt_disconnect_cmd = "AT+ECMTDISC=0"  # 断开指令模板
        self.vbat_mqtt_auth_cmd = "AT+ECMTCONN=0,{client_id},{username},{password}"  # 认证指令模板
        self.vbat_mqtt_wait_time = 2000    # 等待时间(ms)
        
        # 初始化时加载配置
        self.load_vbat_config()
        
        # 初始化时加载设备指令
        self.load_device_commands_from_file()
        
    def load_vbat_config(self):
        """加载VBAT配置"""
        try:
            config = config_manager.load_config('vbat')
            if config:
                # 加载基本配置
                self.vbat_monitor_enabled = config.get('vbat_monitor_enabled', False)
                self.vbat_threshold_low = config.get('vbat_threshold_low', 3300)
                self.vbat_threshold_high = config.get('vbat_threshold_high', 4500)
                self.vbat_max_records = config.get('vbat_max_records', 50)
                
                # 加载MQTT配置
                self.vbat_mqtt_enabled = config.get('vbat_mqtt_enabled', False)
                self.vbat_mqtt_broker = config.get('vbat_mqtt_broker')
                self.vbat_mqtt_port = config.get('vbat_mqtt_port')
                self.vbat_mqtt_client_id = config.get('vbat_mqtt_client_id', 'vbat_monitor_001')
                self.vbat_mqtt_username = config.get('vbat_mqtt_username', '')
                self.vbat_mqtt_password = config.get('vbat_mqtt_password', '')
                self.vbat_mqtt_topic = config.get('vbat_mqtt_topic')
                self.vbat_mqtt_qos = config.get('vbat_mqtt_qos')
                
                # 加载模板格式
                self.vbat_mqtt_connect_template = config.get('vbat_mqtt_connect_template')
                self.vbat_mqtt_subscribe_template = config.get('vbat_mqtt_subscribe_template')
                self.vbat_mqtt_publish_template = config.get('vbat_mqtt_publish_template')
                self.vbat_mqtt_disconnect_template = config.get('vbat_mqtt_disconnect_template')
                self.vbat_mqtt_auth_template = config.get('vbat_mqtt_auth_template')
                
                # 如果没有模板，使用配置文件中的值或默认值
                if not self.vbat_mqtt_connect_template:
                    self.vbat_mqtt_connect_template = config.get('vbat_mqtt_connect_cmd') or 'AT+ECMTOPEN=0,{host_name},{port}'
                if not self.vbat_mqtt_subscribe_template:
                    self.vbat_mqtt_subscribe_template = config.get('vbat_mqtt_subscribe_cmd') or 'AT+ECMTSUB=0,1,{topic},{qos}'
                if not self.vbat_mqtt_publish_template:
                    self.vbat_mqtt_publish_template = config.get('vbat_mqtt_publish_cmd') or 'AT+ECMTPUB=0,0,0,0,{topic},{payload}'
                if not self.vbat_mqtt_disconnect_template:
                    self.vbat_mqtt_disconnect_template = config.get('vbat_mqtt_disconnect_cmd') or 'AT+ECMTDISC=0'
                if not self.vbat_mqtt_auth_template:
                    self.vbat_mqtt_auth_template = config.get('vbat_mqtt_auth_cmd') or 'AT+ECMTCONN=0,{client_id}'
                
                self.vbat_mqtt_wait_time = config.get('vbat_mqtt_wait_time', 2000)
                
                # 初始化网络状态和指令集缓存
                self._last_network_status = None
                self._detected_mt_commands = []
                self._detected_mqtt_commands = []
                self._all_detected_commands = []
                
            # 加载记录
            records = config_manager.load_config('vbat_records')
            if records:
                self.vbat_records = records
                # 限制记录数
                if len(self.vbat_records) > self.vbat_max_records:
                    self.vbat_records = self.vbat_records[-self.vbat_max_records:]
        except Exception as e:
            print(f"❌ 加载VBAT配置失败: {e}")
    
    def save_vbat_config(self):
        """保存VBAT配置"""
        try:
            # 保存模板格式而非实际指令
            config = {
                "vbat_monitor_enabled": self.vbat_monitor_enabled,
                "vbat_threshold_low": self.vbat_threshold_low,
                "vbat_threshold_high": self.vbat_threshold_high,
                "vbat_max_records": self.vbat_max_records,
                "vbat_mqtt_enabled": self.vbat_mqtt_enabled,
                "vbat_mqtt_broker": self.vbat_mqtt_broker,
                "vbat_mqtt_port": self.vbat_mqtt_port,
                "vbat_mqtt_client_id": self.vbat_mqtt_client_id,
                "vbat_mqtt_username": self.vbat_mqtt_username,
                "vbat_mqtt_password": self.vbat_mqtt_password,
                "vbat_mqtt_topic": self.vbat_mqtt_topic,
                "vbat_mqtt_qos": self.vbat_mqtt_qos,
                "vbat_mqtt_connect_template": self.vbat_mqtt_connect_template or "AT+ECMTOPEN=0,{host_name},{port}",
                "vbat_mqtt_subscribe_template": self.vbat_mqtt_subscribe_template or "AT+ECMTSUB=0,1,{topic},{qos}",
                "vbat_mqtt_publish_template": self.vbat_mqtt_publish_template or "AT+ECMTPUB=0,0,0,0,{topic},{payload}",
                "vbat_mqtt_disconnect_template": self.vbat_mqtt_disconnect_template or "AT+ECMTDISC=0",
                "vbat_mqtt_auth_template": self.vbat_mqtt_auth_template or "AT+ECMTCONN=0,{client_id}",
                "vbat_mqtt_wait_time": self.vbat_mqtt_wait_time
            }
            
            # 保存配置和记录
            config_saved = config_manager.save_config('vbat', config)
            records_saved = config_manager.save_config('vbat_records', self.vbat_records)
            
            if config_saved and records_saved:
                print("✅ VBAT配置已保存")
        except Exception as e:
            print(f"❌ 保存VBAT配置失败: {e}")

    def send_all_vbat_records_via_mqtt(self, controller):
        """通过MQTT发送所有VBAT记录"""
        """根据vbat_max_records设置发送指定数量的最新VBAT记录，不足则发送全部"""
        try:
            # 读取所有VBAT记录
            all_records = config_manager.load_config('vbat_records')
            if not all_records:
                print("⚠️ 没有VBAT记录可发送")
                return False
            
            # 根据设置的最大记录数限制发送的记录数
            max_records = self.vbat_max_records
            records_to_send = all_records[-max_records:] if len(all_records) > max_records else all_records
            
            # 获取vbat_records.json文件的动态路径用于显示
            vbat_records_path = config_manager.get_config_path('vbat_records')
            print(f"📊 准备发送vbat_records.json文件内容")
            print(f"📋 文件路径: {vbat_records_path}")
            print(f"📋 文件记录数: {len(all_records)} 条")
            print(f"📋 实际发送记录数: {len(records_to_send)} 条 (根据max_records配置)")
            
            # 获取vbat_records.json文件的动态路径
            vbat_records_path = config_manager.get_config_path('vbat_records')
            
            # 构建发送数据包 - 包含完整的vbat_records.json文件信息
            send_data = {
                "type": "vbat_history_batch",
                "source_file": "vbat_records.json",
                "file_path": str(vbat_records_path),
                "total_records": len(all_records),
                "sent_records": len(records_to_send),
                "max_records_config": max_records,
                "records": records_to_send,
                "complete_file_content": all_records,  # 包含完整文件内容
                "metadata": {
                    "device_id": self.vbat_mqtt_client_id,
                    "send_timestamp": datetime.now().isoformat(),
                    "data_source": "vbat_records.json",
                    "file_info": {
                        "filename": "vbat_records.json",
                        "full_path": str(vbat_records_path),
                        "record_count": len(all_records),
                        "file_size": len(json.dumps(all_records, ensure_ascii=False)) if all_records else 0
                    },
                    "first_record": records_to_send[0]["timestamp"] if records_to_send else None,
                    "last_record": records_to_send[-1]["timestamp"] if records_to_send else None,
                    "voltage_range": {
                        "min": min(r["vbat"] for r in records_to_send) if records_to_send else 0,
                        "max": max(r["vbat"] for r in records_to_send) if records_to_send else 0,
                        "avg": sum(r["vbat"] for r in records_to_send) // len(records_to_send) if records_to_send else 0
                    }
                }
            }
            
            # 使用现有的MQTT发送功能
            success = self.send_vbat_mqtt_alert(
                vbat_mv=records_to_send[-1]["vbat"] if records_to_send else 0,
                alert_type="history_batch",
                controller=controller,
                emergency_mode=False,
                custom_data=send_data
            )
            
            # 获取vbat_records.json文件的动态路径用于显示
            vbat_records_path = config_manager.get_config_path('vbat_records')
            if success:
                print(f"✅ 成功发送vbat_records.json文件内容")
                print(f"✅ 文件: {vbat_records_path}")
                print(f"✅ 记录数: {len(records_to_send)} 条")
            else:
                print("❌ 发送vbat_records.json文件内容失败")
            
            return success
            
        except Exception as e:
            print(f"❌ 发送VBAT记录时出错: {e}")
            return False
    
    def configure_vbat_monitor(self, controller=None):
        """配置VBAT监控参数"""
        print("\n🔋 VBAT监控配置")
        print("=" * 30)
        
        # 显示当前连接信息
        if controller:
            try:
                # 获取当前连接的端口信息
                port_info = "未知端口"
                if hasattr(controller, 'serial_controller') and controller.serial_controller:
                    serial_controller = controller.serial_controller
                    if hasattr(serial_controller, 'serial_port') and serial_controller.serial_port:
                        port = serial_controller.serial_port
                        if port and port.is_open:
                            baudrate = port.baudrate
                            port_info = f"{port.name}@{baudrate}"
                print(f"📡 当前连接: {port_info}")
                print()
            except Exception:
                pass
        
        # 是否启用VBAT监控
        current_state = "启用" if self.vbat_monitor_enabled else "禁用"
        print(f"当前状态: {current_state}")
        choice = input("是否启用VBAT监控? (y/n): ").strip().lower()
        self.vbat_monitor_enabled = choice == 'y'
        
        if self.vbat_monitor_enabled:
            # 设置低阈值
            print(f"当前低阈值: {self.vbat_threshold_low}mV")
            low_input = input("设置低阈值(mV, 默认3300): ").strip()
            if low_input:
                try:
                    self.vbat_threshold_low = max(0, int(low_input))
                except ValueError:
                    self.vbat_threshold_low = 3300
            
            # 设置高阈值
            print(f"当前高阈值: {self.vbat_threshold_high}mV")
            high_input = input("设置高阈值(mV, 默认4500): ").strip()
            if high_input:
                try:
                    self.vbat_threshold_high = max(0, int(high_input))
                except ValueError:
                    self.vbat_threshold_high = 4500
            
            # 设置最大记录数
            print(f"当前最大记录数: {self.vbat_max_records}")
            records_input = input("设置最大记录数(默认50): ").strip()
            if records_input:
                try:
                    new_max_records = max(10, int(records_input))
                    
                    # 检查当前记录数并提示清理
                    current_records = self.vbat_records or []
                    current_count = len(current_records)
                    
                    if current_count > 0:
                        print(f"\n📊 当前已有 {current_count} 条记录")
                        print(f"🔄 新的最大记录数将设置为: {new_max_records}")
                        
                        if current_count > new_max_records:
                            print(f"⚠️  当前记录数({current_count})超过新的最大限制({new_max_records})")
                            choice = input("请选择操作: \n1. 清空所有记录\n2. 保留最新的{}条记录\n请选择 (1/2): ".format(new_max_records)).strip()
                            
                            if choice == "1":
                                # 清空所有记录
                                self.vbat_records = []
                                self.save_vbat_config()
                                print("✅ 已清空所有记录")
                            elif choice == "2":
                                # 保留最新的记录
                                self.vbat_records = current_records[-new_max_records:]
                                self.save_vbat_config()
                                print(f"✅ 已保留最新的 {len(self.vbat_records)} 条记录")
                            else:
                                # 默认保留最新
                                self.vbat_records = current_records[-new_max_records:]
                                self.save_vbat_config()
                                print(f"✅ 默认保留最新的 {len(self.vbat_records)} 条记录")
                        else:
                            print(f"✅ 当前记录数({current_count})未超过新的限制({new_max_records})")
                    
                    self.vbat_max_records = new_max_records
                    
                    # 说明后续数据存储策略
                    print(f"\n📋 数据存储策略:")
                    print(f"   • 最多保留 {self.vbat_max_records} 条最新记录")
                    print(f"   • 当记录数达到上限时，新数据将自动删除最旧的记录")
                    print(f"   • 始终保持记录按时间从新到旧排序")
                    
                except ValueError:
                    self.vbat_max_records = 50
            
            # 🎯 智能配置选择系统
            mqtt_state = "启用" if self.vbat_mqtt_enabled else "禁用"
            print(f"当前MQTT发送: {mqtt_state}")
            mqtt_choice = input("是否启用MQTT发送? (y/n): ").strip().lower()
            self.vbat_mqtt_enabled = mqtt_choice == 'y'
            
            if self.vbat_mqtt_enabled:
                # 🎯 配置选择菜单
                print("\n🎯 配置选择")
                print("=" * 20)
                print("1️⃣ 使用已有配置")
                print("2️⃣ 重新解析配置（从零开始）")
                print()
                
                choice = input("请选择 (1/2): ").strip()
                
                if choice == "1":
                    # 使用已有配置
                    print("\n✅ 使用已有配置:")
                    print(f"   服务器: {self.vbat_mqtt_broker}:{self.vbat_mqtt_port}")
                    print(f"   主题: {self.vbat_mqtt_topic} (QoS {self.vbat_mqtt_qos})")
                    print(f"   连接: {self.vbat_mqtt_connect_cmd}")
                    print(f"   订阅: {self.vbat_mqtt_subscribe_cmd}")
                    print(f"   发布: {self.vbat_mqtt_publish_cmd}")
                    
                elif choice == "2" and controller:
                    # 🎯 交互式逐条解析配置
                    print("\n🎯 交互式逐条解析配置")
                    print("=" * 35)
                    print("💡 逐条输入指令，实时显示解析结果")
                    print("🚪 输入 'q' 退出解析")
                    print()
                    
                    # 重置所有配置为空（真正实现零配置）
                    self.vbat_mqtt_broker = None
                    self.vbat_mqtt_port = None
                    self.vbat_mqtt_topic = None
                    self.vbat_mqtt_qos = None
                    self.vbat_mqtt_connect_cmd = None
                    self.vbat_mqtt_auth_cmd = None
                    self.vbat_mqtt_subscribe_cmd = None
                    self.vbat_mqtt_publish_cmd = None
                    self.vbat_mqtt_disconnect_cmd = None
                    
                    # 🚀 智能解析函数（单次解析）
                    def parse_single_command(input_str):
                        """解析单条指令并返回提取的参数"""
                        
                        result = {
                            "server": None,
                            "port": None,
                            "topic": None,
                            "qos": None,
                            "payload": None,
                            "connect_cmd": None,
                            "auth_cmd": None,
                            "subscribe_cmd": None,
                            "publish_cmd": None,
                            "disconnect_cmd": None,
                            "parsed_params": [],
                            "command_type": None,
                            "template_format": None,
                            "original_command": None
                        }
                        
                        input_str = input_str.strip()
                        if not input_str or input_str.lower() == 'q':
                            return result
                        
                        cmd_upper = input_str.upper()
                        
                        # 解析连接命令
                        if "ECMTOPEN" in cmd_upper:
                            parsed = controller.parse_mqtt_at_command(input_str, "connect")
                            if parsed and parsed.get("broker"):
                                result["server"] = parsed.get("broker")
                                result["port"] = parsed.get("port")
                                result["connect_cmd"] = input_str
                                result["template_format"] = parsed.get("template", "AT+ECMTOPEN=0,{host_name},{port}")
                                result["original_command"] = parsed.get("original", input_str)
                                result["parsed_params"] = [
                                    f"服务器: {parsed['broker']}", 
                                    f"端口: {parsed.get('port', '❌ 未设置')}",
                                    f"模板: {parsed.get('template', 'AT+ECMTOPEN=0,{host_name},{port}')}"
                                ]
                                result["command_type"] = "连接"
                        
                        # 解析订阅命令
                        elif "ECMTSUB" in cmd_upper:
                            parsed = controller.parse_mqtt_at_command(input_str, "subscribe")
                            if parsed and parsed.get("topic"):
                                result["topic"] = parsed.get("topic")
                                result["qos"] = parsed.get("qos")
                                result["subscribe_cmd"] = input_str
                                result["template_format"] = parsed.get("template", "AT+ECMTSUB=0,1,{topic},{qos}")
                                result["original_command"] = parsed.get("original", input_str)
                                result["parsed_params"] = [
                                    f"主题: {parsed['topic']}", 
                                    f"QoS: {parsed.get('qos', '❌ 未设置')}",
                                    f"模板: {parsed.get('template', 'AT+ECMTSUB=0,1,{topic},{qos}')}"
                                ]
                                result["command_type"] = "订阅"
                        
                        # 解析发布命令
                        elif "ECMTPUB" in cmd_upper:
                            parsed = controller.parse_mqtt_at_command(input_str, "publish")
                            if parsed and parsed.get("topic"):
                                result["topic"] = parsed.get("topic")
                                match = re.search(r'"([^"]*)"$', input_str)
                                if match:
                                    result["payload"] = match.group(1)
                                result["publish_cmd"] = input_str
                                result["template_format"] = parsed.get("template", "AT+ECMTPUB=0,1,1,0,{topic},{qos},{payload}")
                                result["original_command"] = parsed.get("original", input_str)
                                result["parsed_params"] = [
                                    f"主题: {parsed['topic']}", 
                                    f"消息: {result['payload']}",
                                    f"模板: {parsed.get('template', 'AT+ECMTPUB=0,1,1,0,{topic},{qos},{payload}')}"
                                ]
                                result["command_type"] = "发布"
                        
                        # 解析认证命令
                        elif "ECMTCONN" in cmd_upper:
                            parsed = controller.parse_mqtt_at_command(input_str, "auth")
                            if parsed:
                                client_id = parsed.get("client_id", "")
                                username = parsed.get("username", "")
                                password = parsed.get("password", "")
                                result["auth_cmd"] = input_str
                                result["template_format"] = parsed.get("template", "AT+ECMTCONN=0,{client_id},{username},{password}")
                                result["original_command"] = parsed.get("original", input_str)
                                result["parsed_params"] = [
                                    f"客户端ID: {client_id}", 
                                    f"用户名: {username}", 
                                    f"密码: {'******' if password else '(无)'}",
                                    f"模板: {parsed.get('template', 'AT+ECMTCONN=0,{client_id},{username},{password}')}"
                                ]
                                result["command_type"] = "认证"
                        
                        # 解析断开连接命令 (ECMTDISC 或 ECMTCLOSE)
                        elif "ECMTDISC" in cmd_upper or "ECMTCLOSE" in cmd_upper:
                            parsed = controller.parse_mqtt_at_command(input_str, "disconnect")
                            if parsed:
                                client_id = parsed.get("client_id", "")
                                result["disconnect_cmd"] = input_str
                                result["template_format"] = parsed.get("template", "AT+ECMTDISC=0")
                                result["original_command"] = parsed.get("original", input_str)
                                result["parsed_params"] = [
                                    f"客户端ID: {client_id}",
                                    f"模板: {parsed.get('template', 'AT+ECMTDISC=0')}"
                                ]
                                result["command_type"] = "断开连接"
                        
                        # 解析极简格式
                        elif ";" in input_str and not input_str.startswith("AT"):
                            parts = [p.strip() for p in input_str.split(";" if input_str else "") if p.strip()]
                            if len(parts) >= 2:
                                # 服务器和端口
                                server_port = parts[0].split(",")
                                if server_port[0].strip():
                                    result["server"] = server_port[0].strip()
                                    result["port"] = int(server_port[1]) if len(server_port) > 1 else None
                                    result["connect_cmd"] = f'AT+ECMTOPEN=0,"{result["server"]}",{result["port"]}'
                                    result["parsed_params"].extend([f"服务器: {result['server']}", f"端口: {result['port']}"])
                                    result["command_type"] = "连接"
                                
                                # 主题和QoS
                                topic_qos = parts[1].split(",")
                                if topic_qos[0].strip() and len(topic_qos) > 1:
                                    result["topic"] = topic_qos[0].strip()
                                    result["qos"] = int(topic_qos[1])
                                    result["subscribe_cmd"] = f'AT+ECMTSUB=0,1,"{result["topic"]}",{result["qos"]}'
                                    result["parsed_params"].extend([f"主题: {result['topic']}", f"QoS: {result['qos']}"])
                                    if result["command_type"]:
                                        result["command_type"] += "+订阅"
                                    else:
                                        result["command_type"] = "订阅"
                                
                                # 消息模板
                                if len(parts) >= 3 and parts[2].strip():
                                    result["payload"] = parts[2].strip()
                                    result["publish_cmd"] = f'AT+ECMTPUB=0,0,0,0,"{result["topic"]}","{result["payload"]}"' 
                                    result["parsed_params"].append(f"消息: {result['payload']}")
                                    if result["command_type"]:
                                        result["command_type"] += "+发布"
                                    else:
                                        result["command_type"] = "发布"
                        
                        return result
                    
                    # 🎯 交互式解析主循环
                    while True:
                        # 显示当前配置状态
                        print("\n📊 当前配置:")
                        print(f"   🌐 服务器: {self.vbat_mqtt_broker or '❌ 未设置'}")
                        print(f"   🔢 端口: {self.vbat_mqtt_port or '❌ 未设置'}")
                        print(f"   📡 主题: {self.vbat_mqtt_topic or '❌ 未设置'}")
                        print(f"   📊 QoS: {self.vbat_mqtt_qos or '❌ 未设置'}")
                        print(f"   🔗 连接: {self.vbat_mqtt_connect_cmd or '❌ 未设置'}")
                        print(f"   🔐 认证: {self.vbat_mqtt_auth_cmd or '❌ 未设置'}")
                        print(f"   📋 订阅: {self.vbat_mqtt_subscribe_cmd or '❌ 未设置'}")
                        print(f"   📤 发布: {self.vbat_mqtt_publish_cmd or '❌ 未设置'}")
                        print(f"   🚫 断开: {self.vbat_mqtt_disconnect_cmd or '❌ 未设置'}")
                        
                        # 显示缺失的配置（按连接、认证、订阅、发布顺序）
                        missing = []
                        if not self.vbat_mqtt_connect_cmd:
                            missing.append("连接命令")
                        if not self.vbat_mqtt_auth_cmd:
                            missing.append("认证命令")
                        if not self.vbat_mqtt_subscribe_cmd:
                            missing.append("订阅命令")
                        if not self.vbat_mqtt_publish_cmd:
                            missing.append("发布命令")
                        # 基本参数检查
                        if not self.vbat_mqtt_broker:
                            missing.append("服务器地址")
                        if not self.vbat_mqtt_topic:
                            missing.append("主题")
                        
                        if not missing:
                            print("\n✅ 配置已完成!")
                            print("� 完整命令:")
                            print(f"   🔗 连接: {self.vbat_mqtt_connect_cmd}")
                            print(f"   🔐 认证: {self.vbat_mqtt_auth_cmd}")
                            print(f"   📋 订阅: {self.vbat_mqtt_subscribe_cmd}")
                            print(f"   📤 发布: {self.vbat_mqtt_publish_cmd}")
                            print(f"   🚫 断开: {self.vbat_mqtt_disconnect_cmd}")
                            
                            # 🏷️ 配置命名和保存系统
                            print("\n🏷️ 保存当前配置")
                            print("=" * 20)
                            config_name = input("请输入配置名称(直接回车跳过保存): ").strip()
                            
                            if config_name:
                                # 创建命名配置
                                named_config = {
                                    "name": config_name,
                                    "server": self.vbat_mqtt_broker,
                                    "port": self.vbat_mqtt_port,
                                    "topic": self.vbat_mqtt_topic,
                                    "qos": self.vbat_mqtt_qos,
                                    "connect_cmd": self.vbat_mqtt_connect_cmd,
                                    "auth_cmd": self.vbat_mqtt_auth_cmd,
                                    "subscribe_cmd": self.vbat_mqtt_subscribe_cmd,
                                    "publish_cmd": self.vbat_mqtt_publish_cmd,
                                    "disconnect_cmd": self.vbat_mqtt_disconnect_cmd,
                                    "message_template": self.vbat_mqtt_publish_cmd.split('"')[-2] if self.vbat_mqtt_publish_cmd else '{"vbat":{vbat}}',
                                    "created_at": datetime.now().isoformat()
                                }
                                # 设置为交互式保存模式
                                self.interactive_save = True
                                # 保存配置并检查结果
                                if self.save_named_config(named_config):
                                    print(f"✅ 配置 '{config_name}' 已保存")
                                # 重置交互式保存标志
                                self.interactive_save = False
                            break
                        
                        print(f"\n❌ 缺失配置: {', '.join(missing)}")
                        print("\n💡 请输入一条指令进行解析 (输入 'q' 退出):")
                        input_str = input(" > ").strip()
                        
                        if input_str.lower() == 'q':
                            break
                        
                        # 解析指令
                        parsed_result = parse_single_command(input_str)
                        
                        # 显示解析结果
                        if parsed_result.get("command_type"):
                            print(f"\n✅ 解析成功 ({parsed_result['command_type']}):")
                            for param in parsed_result["parsed_params"]:
                                print(f"   ✅ {param}")
                            
                            # 更新配置
                            if parsed_result["server"] and not self.vbat_mqtt_broker:
                                self.vbat_mqtt_broker = parsed_result["server"]
                            if parsed_result["port"] and not self.vbat_mqtt_port:
                                self.vbat_mqtt_port = parsed_result["port"]
                            if parsed_result["topic"] and not self.vbat_mqtt_topic:
                                self.vbat_mqtt_topic = parsed_result["topic"]
                            if parsed_result["qos"] is not None and self.vbat_mqtt_qos is None:
                                self.vbat_mqtt_qos = parsed_result["qos"]
                            if parsed_result["connect_cmd"] and not self.vbat_mqtt_connect_cmd:
                                self.vbat_mqtt_connect_cmd = parsed_result["connect_cmd"]
                            if parsed_result["auth_cmd"] and not self.vbat_mqtt_auth_cmd:
                                self.vbat_mqtt_auth_cmd = parsed_result["auth_cmd"]
                            if parsed_result["subscribe_cmd"] and not self.vbat_mqtt_subscribe_cmd:
                                self.vbat_mqtt_subscribe_cmd = parsed_result["subscribe_cmd"]
                            if parsed_result["publish_cmd"] and not self.vbat_mqtt_publish_cmd:
                                self.vbat_mqtt_publish_cmd = parsed_result["publish_cmd"]
                            if parsed_result["disconnect_cmd"] and not self.vbat_mqtt_disconnect_cmd:
                                self.vbat_mqtt_disconnect_cmd = parsed_result["disconnect_cmd"]
                        else:
                            print("❌ 解析失败，请输入有效的AT指令或极简格式")
                            print("💡 示例: AT+ECMTOPEN=0,\"test.mosquitto.org\",1883")
                            print("💡 或: test.mosquitto.org,1883;/lierda/test,2;{\"vbat\":{vbat}}")
        
        # 保存配置
        self.save_vbat_config()
        print("✅ VBAT监控配置已完成")
    
    def process_vbat_value(self, vbat_value, controller=None):
        """处理VBAT值并检查阈值"""
        try:
            vbat_mv = int(vbat_value)
            
            # 记录数据
            record = {
                "timestamp": datetime.now().isoformat(),
                "vbat": vbat_mv
            }
            self.vbat_records.append(record)
            
            # 限制记录数
            if len(self.vbat_records) > self.vbat_max_records:
                self.vbat_records = self.vbat_records[-self.vbat_max_records:]
            
            # 保存记录
            self.save_vbat_config()
            
            # 检查阈值
            alert_type = None
            emergency_stop = False
            if vbat_mv <= self.vbat_threshold_low:
                alert_type = "low"
                print(f"⚠️ VBAT低电压警告: {vbat_mv}mV (阈值: {self.vbat_threshold_low}mV)")
            elif vbat_mv >= self.vbat_threshold_high:
                alert_type = "high"
                emergency_stop = True  # 高压警告触发紧急停止
                print(f"⚠️ VBAT高电压警告: {vbat_mv}mV (阈值: {self.vbat_threshold_high}mV)")
                print(f"🚨 紧急中断：检测到高压，正在停止循环操作...")
            
            # 发送MQTT告警
            if alert_type and self.vbat_mqtt_enabled and controller:
                # 高压警告时先上报数据，再让用户选择
                if emergency_stop:
                    # 先执行紧急停止并上报数据
                    self.emergency_stop_and_report(controller)
                    
                    # 上报完成后让用户选择是否继续
                    user_choice = self._handle_emergency_stop_choice(vbat_mv, controller)
                    if user_choice == 'continue':
                        print("⚠️ 用户选择继续操作")
                        emergency_stop = False  # 取消紧急停止
                        # 重置紧急停止标志以继续操作
                        if hasattr(controller, 'emergency_stop_flag'):
                            controller.emergency_stop_flag = False
                        return  # 继续循环
                    else:
                        return  # 默认停止，保持紧急停止状态
                else:
                    self.send_vbat_mqtt_alert(vbat_mv, alert_type, controller)
                
        except ValueError:
            pass
    
    def _handle_emergency_stop_choice(self, vbat_mv, controller):
        """处理紧急停止时的用户选择（数据上报完成后）"""
        try:
            print(f"\n🚨 紧急停止 - 数据已上报完成")
            print(f"   当前VBAT: {vbat_mv}mV")
            print(f"   高阈值: {self.vbat_threshold_high}mV")
            print()
            print("请选择后续操作:")
            print("1️⃣ 继续测试 (Continue)")
            print("2️⃣ 保持停止 (Stop)")
            print("3️⃣ 查看详细状态 (Detail)")
            print()
            
            while True:
                choice = input("请输入选择 [C/S/D]: ").strip().upper()
                
                if choice in ['C', 'CONTINUE', '继续']:
                    return 'continue'
                elif choice in ['S', 'STOP', '停止']:
                    return 'stop'
                elif choice in ['D', 'DETAIL', '详细']:
                    # 显示详细状态
                    self._show_emergency_status(vbat_mv, controller)
                else:
                    print("❌ 无效选择，请重新输入")
                    
        except KeyboardInterrupt:
            print("\n❌ 用户中断，默认停止操作")
            return 'stop'
        except Exception:
            return 'stop'  # 默认停止
    
    def _show_emergency_status(self, vbat_mv, controller):
        """显示紧急状态的详细信息"""
        print(f"\n📊 详细状态:")
        print(f"   • VBAT: {vbat_mv}mV")
        print(f"   • 低阈值: {self.vbat_threshold_low}mV")
        print(f"   • 高阈值: {self.vbat_threshold_high}mV")
        print(f"   • MQTT发送: {'启用' if self.vbat_mqtt_enabled else '禁用'}")
        
        if hasattr(controller, 'serial_controller') and controller.serial_controller:
            serial_port = controller.serial_controller.serial_port
            if serial_port and serial_port.is_open:
                print(f"   • 串口连接: {serial_port.name}@{serial_port.baudrate}")
        
        print(f"   • 记录数: {len(self.vbat_records)}")
        print()
    
    def emergency_stop_and_report(self, controller):
        """紧急停止并上报数据"""
        try:
            # 1. 立即停止继电器操作（保持停止状态）
            if hasattr(controller, 'relay_controller'):
                relay_controller = controller.relay_controller
                if hasattr(relay_controller, 'stop_all_relays'):
                    relay_controller.stop_all_relays()
                    print("🛑 继电器已停止操作，保持当前状态")
            
            # 2. 发送完整的VBAT历史数据
            mqtt_success = False
            if self.vbat_records:
                latest_record = self.vbat_records[-1]
                vbat_mv = latest_record["vbat"]
                mqtt_success = self.send_vbat_mqtt_alert(vbat_mv, "emergency", controller, emergency_mode=True)
            else:
                print("⚠️ 没有VBAT记录，无法上报数据")
            
            # 3. 设置中断标志
            if hasattr(controller, 'emergency_stop_flag'):
                controller.emergency_stop_flag = True
            
            # 根据MQTT发送结果、网络状态和配置情况进行智能提示
            if mqtt_success:
                print("✅ 紧急中断完成，数据已上报")
            else:
                # 如果还没有网络状态，尝试获取当前网络状态
                if not self._last_network_status or self._last_network_status == "网络状态未检测":
                    # 尝试主动获取网络状态
                    try:
                        if hasattr(controller, 'serial_controller') and controller.serial_controller.serial_port:
                            serial_port = controller.serial_controller.serial_port
                            if serial_port and serial_port.is_open:
                                cereg_cmd = "AT+CEREG?\r\n"
                                serial_port.write(cereg_cmd.encode('utf-8'))
                                time.sleep(1)
                                
                                if serial_port.in_waiting > 0:
                                    response = serial_port.read(serial_port.in_waiting).decode('utf-8', errors='ignore')
                                    
                                    if "+CEREG:" in response:
                                        lines = response.split('\n')
                                        for line in lines:
                                            line = line.strip()
                                            if line.startswith('+CEREG:'):
                                                try:
                                                    parts = line.split(':')[1].split(',')
                                                    if len(parts) >= 2:
                                                        stat = int(parts[1].strip())
                                                        status_map = {
                                                            0: "未注册，未搜索网络",
                                                            1: "已注册，本地网络",
                                                            2: "未注册，正在搜索网络",
                                                            3: "注册被拒绝",
                                                            4: "未知状态",
                                                            5: "已注册，漫游网络"
                                                        }
                                                        self._last_network_status = status_map.get(stat, f"未知状态({stat})")
                                                        break
                                                except (IndexError, ValueError):
                                                    self._last_network_status = f"网络状态解析失败: {line}"
                    except Exception:
                        pass  # 忽略主动获取网络状态的错误
                
                # 获取当前网络状态和配置信息
                network_status = self._get_network_status()
                config_info = self._get_mqtt_config_info()
                
                print("⚠️ 紧急中断完成，但数据上报失败")
                
                # 显示网络状态
                if network_status:
                    print(f"   🌐 网络状态: {network_status}")
                
                # 显示配置信息和建议（表格格式）
                if config_info:
                    print(f"   ⚙️ 配置检查: {config_info['summary']}")
                    
                    # 显示配置信息（简洁列表格式）
                    if 'format' in config_info:
                        print("\n   📋 当前配置:")
                        for key, value in config_info['format'].items():
                            if key != '配置项':  # 跳过表头
                                print(f"   • {key}: {value}")
                    
                    # 重新读取设备指令并保存到文件
                    print("\n   🔄 重新检测设备指令...")
                    try:
                        # 尝试从设备重新读取指令
                        if hasattr(self, 'serial_controller') and self.serial_controller.serial_port and self.serial_controller.serial_port.is_open:
                            # 直接使用现有的serial_controller进行指令检测
                            print("   📡 正在检测设备指令...")
                            
                            # 调用内部检测方法
                            success = self._internal_detect_device_commands()
                            if success:
                                print("   ✅ 设备指令已更新并保存")
                            else:
                                print("   ⚠️ 指令检测失败，尝试从文件加载已保存的指令")
                        else:
                            print("   ⚠️ 设备未连接，尝试从文件加载已保存的指令")
                    except Exception as e:
                        print(f"   ⚠️ 指令检测失败: {e}")
                    
                    # 重新获取配置信息（包含最新的指令匹配检查）
                    try:
                        config_info = self._get_mqtt_config_info()
                    except Exception as e:
                        print(f"   ⚠️ 配置检查失败: {e}")
                    
                    # 显示指令匹配检查
                    if 'command_match' in config_info:
                        match_data = config_info['command_match']
                        print(f"\n   🔍 指令匹配状态: {match_data.get('status', '未知')}")
                        
                        if match_data.get('mismatches'):
                            print("   ⚠️ 不匹配的指令:")
                            for mismatch in match_data['mismatches']:
                                print(f"      • {mismatch} (请检查设备是否支持)")
                        
                        if match_data.get('matches'):
                            print(f"   ✅ 匹配的指令: {', '.join(match_data['matches'])}")
                    
                    # 显示设备指令统计
                    if hasattr(self, '_detected_mqtt_commands') and hasattr(self, '_detected_mt_commands'):
                        print(f"\n   📱 设备指令支持:")
                        print(f"      • MQTT相关: {len(self._detected_mqtt_commands)}条")
                        if self._detected_mqtt_commands:
                            print(f"        示例: {', '.join(self._detected_mqtt_commands[:3])}")
                        print(f"      • MT相关: {len(self._detected_mt_commands)}条")
                        if self._detected_mt_commands:
                            print(f"        示例: {', '.join(self._detected_mt_commands[:3])}")
                    
                    if config_info.get('suggestions'):
                        print(f"\n   💡 建议: {config_info['suggestions']}")
            
        except Exception as e:
            print(f"❌ 紧急中断失败: {e}")
    
    def send_vbat_mqtt_alert(self, vbat_mv, alert_type, controller, emergency_mode=False, custom_data=None, max_retries=2):
        """发送VBAT MQTT告警（支持自定义指令模板）"""
        try:
            # 从控制器获取serial_port
            serial_port = None
            if hasattr(controller, 'serial_controller'):
                serial_controller = controller.serial_controller
                if hasattr(serial_controller, 'serial_port'):
                    serial_port = serial_controller.serial_port
            
            if not serial_port or not serial_port.is_open:
                print("❌ 端口未连接，无法发送MQTT告警")
                return
                
            # 显示端口和波特率信息
            port_info = f"{serial_port.name}@{serial_port.baudrate}" if serial_port else "未知端口"
            if custom_data is not None and custom_data.get("type") == "vbat_history_batch":
                print(f"📡 准备发送VBAT历史记录 - {port_info}")
            else:
                print(f"📡 准备发送MQTT告警: {vbat_mv}mV ({alert_type}) - {port_info}")
            
            # 构建打包数据结构
            current_time = datetime.now().isoformat()
            
            # 获取完整的历史数据
            max_records = getattr(self, 'vbat_max_records', 100)
            history_package = []
            if len(self.vbat_records) > 0:
                history_package = self.vbat_records[-max_records:]
            
            # 直接读取vbat_records.json文件内容作为payload
            try:
                with open('config/vbat_records.json', 'r', encoding='utf-8') as f:
                    vbat_data = json.load(f)
                package_json = json.dumps(vbat_data, ensure_ascii=False, separators=(',', ':'))
            except Exception as e:
                print(f"❌ 读取vbat_records.json失败: {e}")
                # 如果文件读取失败，使用历史数据
                package_json = json.dumps(history_package, ensure_ascii=False, separators=(',', ':'))
            
            # 获取配置参数
            broker = getattr(self, 'vbat_mqtt_broker', None)
            port = getattr(self, 'vbat_mqtt_port', None)
            topic = getattr(self, 'vbat_mqtt_topic', None)
            qos = getattr(self, 'vbat_mqtt_qos', None)
            wait_time = getattr(self, 'vbat_mqtt_wait_time', 2000)
            
            # 获取认证配置
            client_id = getattr(self, 'vbat_mqtt_client_id', '')
            username = getattr(self, 'vbat_mqtt_username', '')
            password = getattr(self, 'vbat_mqtt_password', '')
            
            # 获取原始配置值以检查是否真正配置了（不使用默认值）
            config = config_manager.load_config('vbat')
            raw_connect_cmd = config.get('vbat_mqtt_connect_cmd') if config else None
            raw_subscribe_cmd = config.get('vbat_mqtt_subscribe_cmd') if config else None
            raw_publish_cmd = config.get('vbat_mqtt_publish_cmd') if config else None
            
            # 检查配置完整性
            if not all([broker, port, topic, qos]):
                missing_configs = []
                if not broker: missing_configs.append("服务器地址")
                if not port: missing_configs.append("端口")
                if not topic: missing_configs.append("主题")
                if not qos: missing_configs.append("QoS等级")
                if not client_id: missing_configs.append("客户端ID")
                if not raw_connect_cmd: missing_configs.append("连接命令")
                if not raw_subscribe_cmd: missing_configs.append("订阅命令")
                if not raw_publish_cmd: missing_configs.append("发布命令")
                
                print(f"❌ MQTT配置不完整，无法发送告警。缺少：{', '.join(missing_configs)}")
                print("💡 请先通过VBAT监控配置完成MQTT设置")
                return
            
            # 获取指令模板，优先使用模板变量
            connect_template = getattr(self, 'vbat_mqtt_connect_template', '') or 'AT+ECMTOPEN=0,{host_name},{port}'
            auth_template = getattr(self, 'vbat_mqtt_auth_template', '') or 'AT+ECMTCONN=0,{client_id}'
            subscribe_template = getattr(self, 'vbat_mqtt_subscribe_template', '') or 'AT+ECMTSUB=0,1,{topic},{qos}'
            publish_template = getattr(self, 'vbat_mqtt_publish_template', '') or 'AT+ECMTPUB=0,0,0,0,{topic},{payload}'
            disconnect_template = getattr(self, 'vbat_mqtt_disconnect_template', '') or 'AT+ECMTDISC=0'
            
            # 先检查网络连接状态
            print("🌐 检查网络连接状态...")
            try:
                cereg_cmd = "AT+CEREG?\r\n"
                serial_port.write(cereg_cmd.encode('utf-8'))
                time.sleep(1)
                
                network_ready = False
                if serial_port.in_waiting > 0:
                    response = serial_port.read(serial_port.in_waiting).decode('utf-8', errors='ignore')
                    
                    # 解析CEREG响应并保存状态
                    if "+CEREG:" in response:
                        lines = response.split('\n')
                        for line in lines:
                            line = line.strip()
                            if line.startswith('+CEREG:'):
                                try:
                                    # 解析格式: +CEREG: <n>,<stat>[,<tac>,<ci>,<AcT>]
                                    parts = line.split(':')[1].split(',')
                                    if len(parts) >= 2:
                                        stat = int(parts[1].strip())
                                        status_map = {
                                            0: "未注册，未搜索网络",
                                            1: "已注册，本地网络",
                                            2: "未注册，正在搜索网络",
                                            3: "注册被拒绝",
                                            4: "未知状态",
                                            5: "已注册，漫游网络"
                                        }
                                        status_text = status_map.get(stat, f"未知状态({stat})")
                                        
                                        # 保存网络状态到实例属性
                                        self._last_network_status = status_text
                                        
                                        if stat in [1, 5]:
                                            print(f"   ✅ 网络状态: {status_text}")
                                            network_ready = True
                                        else:
                                            print(f"   ❌ 网络状态: {status_text}")
                                            
                                except (IndexError, ValueError):
                                    error_text = f"网络状态解析失败: {line}"
                                    self._last_network_status = error_text
                                    print(f"   ⚠️ {error_text}")
                    else:
                        no_response_text = "无法获取网络注册状态"
                        self._last_network_status = no_response_text
                        print(f"   ⚠️ {no_response_text}")
                else:
                    no_data_text = "无网络状态响应"
                    self._last_network_status = no_data_text
                    print(f"   ⚠️ {no_data_text}")
                
                if not network_ready:
                    print("⏸️ 网络未就绪，跳过MQTT发送...")
                    return
                    
            except Exception as cereg_error:
                error_msg = f"网络连接检查失败: {cereg_error}"
                self._last_network_status = error_msg
                print(f"⚠️ {error_msg}")
                print("⚠️ 继续尝试MQTT连接...")
            
            # 替换模板变量，添加错误处理
            try:
                connect_cmd = connect_template.format(host_name=f'"{broker}"', port=port) + '\r\n'
                
                # 根据用户名密码情况生成认证指令
                if username and password:
                    auth_cmd = auth_template.format(client_id=f'"{client_id}"', username=f'"{username}"', password=f'"{password}"') + '\r\n'
                elif username:
                    auth_cmd = f'AT+ECMTCONN=0,"{client_id}","{username}"\r\n'
                else:
                    auth_cmd = f'AT+ECMTCONN=0,"{client_id}"\r\n'
                    
                subscribe_cmd = subscribe_template.format(topic=f'"{topic}"', qos=qos) + '\r\n'
                # 生成JSON字符串并移除所有反斜杠
                json_payload = json.dumps(package_json, ensure_ascii=False)
                # 直接移除所有反斜杠字符
                clean_payload = json_payload.replace('\\', '')
                publish_cmd = publish_template.format(topic=f'"{topic}"', payload=clean_payload) + '\r\n'
                disconnect_cmd = disconnect_template.format() + '\r\n'
            except (AttributeError, KeyError) as e:
                print(f"❌ MQTT模板格式错误: {e}")
                return
            
            # 1. 连接MQTT服务器
            print("🔗 连接MQTT服务器...")
            print(f"   指令: {connect_cmd.strip()}")
            serial_port.write(connect_cmd.encode('utf-8'))
            
            # 初始化成功计数器
            success_count = 0
            # 动态计算总步骤数：连接(1) + 认证(1) + 订阅(1) + 发布(1) + 断开连接(可选)
            total_steps = 4  # 基础步骤：连接、认证、订阅、发布
            if disconnect_template and disconnect_template.strip():
                total_steps += 1  # 断开连接步骤
            
            # 等待连接响应
            time.sleep(wait_time / 1000)
            connect_response = ""
            start_time = time.time()
            
            # 等待最多3秒获取连接响应
            while time.time() - start_time < 3:
                if serial_port.in_waiting > 0:
                    chunk = serial_port.read(serial_port.in_waiting).decode('utf-8', errors='ignore')
                    connect_response += chunk
                    if 'OK' in connect_response.upper() or 'ERROR' in connect_response.upper():
                        break
                time.sleep(0.1)
            
            print(f"   ← 响应: {connect_response.strip()}")
            if self._check_command_success(connect_response, connect_cmd):
                expected_pattern = self._get_expected_response_pattern(connect_cmd)
                has_expected_pattern = expected_pattern in connect_response.upper()
                
                if has_expected_pattern and expected_pattern != 'OK':
                    pattern_text = f" ({expected_pattern}确认)"
                    print(f"   ✅ 连接服务器成功{pattern_text}")
                else:
                    print(f"   ✅ 连接服务器成功")
                success_count += 1
            else:
                print(f"   ❌ 连接服务器失败")
                print(f"   📄 失败响应: {connect_response.strip()}")
                
                # 检查重试次数
                if max_retries <= 1:
                    print("❌ 达到最大重试次数，放弃发送")
                    return False
                    
                # 连接失败也重试整个流程
                print("🔄 连接失败，准备重试整个流程...")
                
                # 断开连接（如果可能）
                if disconnect_template and disconnect_template.strip():
                    try:
                        serial_port.write(disconnect_cmd.encode('utf-8'))
                        time.sleep(2)
                        if serial_port.in_waiting > 0:
                            serial_port.read(serial_port.in_waiting)
                    except Exception as e:
                        print(f"⚠️ 断开连接时出错: {e}")
                
                # 递归调用重试整个流程
                return self.send_vbat_mqtt_alert(vbat_mv, alert_type, controller, emergency_mode, custom_data, max_retries - 1)

            # 1.5 添加ECMTCONN指令（认证连接）
            print("🔒 发送MQTT认证指令...")
            print(f"   指令: {auth_cmd.strip()}")
            serial_port.write(auth_cmd.encode('utf-8'))
            
            # 等待认证响应
            auth_response = ""
            start_time = time.time()
            
            # 等待最多3秒获取认证响应
            while time.time() - start_time < 3:
                if serial_port.in_waiting > 0:
                    chunk = serial_port.read(serial_port.in_waiting).decode('utf-8', errors='ignore')
                    auth_response += chunk
                    if 'OK' in auth_response.upper() or 'ERROR' in auth_response.upper():
                        break
                time.sleep(0.1)
            
            print(f"   ← 响应: {auth_response.strip()}")
            if self._check_command_success(auth_response, auth_cmd):
                expected_pattern = self._get_expected_response_pattern(auth_cmd)
                has_expected_pattern = expected_pattern in auth_response.upper()
                
                if has_expected_pattern and expected_pattern != 'OK':
                    pattern_text = f" ({expected_pattern}确认)"
                    print(f"   ✅ 认证成功{pattern_text}")
                else:
                    print(f"   ✅ 认证成功")
                success_count += 1
            else:
                print(f"   ❌ 认证失败")
                print(f"   📄 失败响应: {auth_response.strip()}")
                
                # 检查重试次数
                if max_retries <= 1:
                    print("❌ 达到最大重试次数，放弃发送")
                    return False
                    
                # 认证失败也重试整个流程
                print("🔄 认证失败，准备重试整个流程...")
                
                # 断开连接
                if disconnect_template and disconnect_template.strip():
                    try:
                        serial_port.write(disconnect_cmd.encode('utf-8'))
                        time.sleep(2)
                        if serial_port.in_waiting > 0:
                            serial_port.read(serial_port.in_waiting)
                    except Exception as e:
                        print(f"⚠️ 断开连接时出错: {e}")
                
                # 递归调用重试整个流程
                return self.send_vbat_mqtt_alert(vbat_mv, alert_type, controller, emergency_mode, custom_data, max_retries - 1)

            # 2. 订阅主题
            print("📥 订阅主题...")
            print(f"   指令: {subscribe_cmd.strip()}")
            serial_port.write(subscribe_cmd.encode('utf-8'))
            
            # 等待订阅响应
            subscribe_response = ""
            start_time = time.time()
            
            # 等待最多3秒获取订阅响应
            while time.time() - start_time < 3:
                if serial_port.in_waiting > 0:
                    chunk = serial_port.read(serial_port.in_waiting).decode('utf-8', errors='ignore')
                    subscribe_response += chunk
                    if 'OK' in subscribe_response.upper() or 'ERROR' in subscribe_response.upper():
                        break
                time.sleep(0.1)
            
            print(f"   ← 响应: {subscribe_response.strip()}")
            if self._check_command_success(subscribe_response, subscribe_cmd):
                expected_pattern = self._get_expected_response_pattern(subscribe_cmd)
                has_expected_pattern = expected_pattern in subscribe_response.upper()
                
                if has_expected_pattern and expected_pattern != 'OK':
                    pattern_text = f" ({expected_pattern}确认)"
                    print(f"   ✅ 订阅成功{pattern_text}")
                else:
                    print(f"   ✅ 订阅成功")
                success_count += 1
            else:
                print(f"   ❌ 订阅失败")
                print(f"   📄 失败响应: {subscribe_response.strip()}")
                
                # 遇到订阅错误，先断开连接然后重试
                print("🔄 订阅失败，准备重试整个流程...")
                
                # 断开连接
                if disconnect_template and disconnect_template.strip():
                    print("🔗 断开MQTT连接...")
                    try:
                        serial_port.write(disconnect_cmd.encode('utf-8'))
                        time.sleep(1)
                        # 清空缓冲区
                        if serial_port.in_waiting > 0:
                            serial_port.read(serial_port.in_waiting)
                    except Exception as e:
                        print(f"⚠️ 断开连接时出错: {e}")
                
                # 等待一小段时间后重试
                time.sleep(2)
                print("🔄 重新开始MQTT流程...")
                
                # 递归调用自身重试整个流程
                return self.send_vbat_mqtt_alert(vbat_mv, alert_type, controller, emergency_mode, custom_data, max_retries-1)
            
            # 3. 发布消息
            print("📤 发布消息...")
            print(f"   主题: {topic}")
            print(f"   数据包大小: {len(package_json)}字符")
            serial_port.write(publish_cmd.encode('utf-8'))
            
            # 等待并读取发布响应
            print("   ⏳ 等待发布响应...")
            start_time = time.time()
            publish_response = ""
            
            # 等待最多5秒获取完整响应
            while time.time() - start_time < 5:
                if serial_port.in_waiting > 0:
                    chunk = serial_port.read(serial_port.in_waiting).decode('utf-8', errors='ignore')
                    publish_response += chunk
                    
                    # 检查是否收到完整的响应
                    if 'OK' in publish_response.upper() or 'ERROR' in publish_response.upper():
                        break
                        
                time.sleep(0.1)
            
            print(f"   ← 完整响应: {publish_response.strip()}")
            if self._check_command_success(publish_response, publish_cmd):
                expected_pattern = self._get_expected_response_pattern(publish_cmd)
                has_expected_pattern = expected_pattern in publish_response.upper()
                
                if has_expected_pattern and expected_pattern != 'OK':
                    pattern_text = f" ({expected_pattern}确认)"
                    print(f"   ✅ 发布成功{pattern_text}")
                else:
                    print(f"   ✅ 发布成功")
                success_count += 1
            else:
                print(f"   ❌ 发布失败")
                print(f"   📄 失败响应: {publish_response.strip()}")
                
                # 检查重试次数
                if max_retries <= 1:
                    print("❌ 达到最大重试次数，放弃发送")
                    return False
                    
                # 发布失败也重试整个流程
                print("🔄 发布失败，断开MQTT连接并重试...")
                
                # 断开连接
                if disconnect_template and disconnect_template.strip():
                    try:
                        serial_port.write(disconnect_cmd.encode('utf-8'))
                        time.sleep(2)  # 等待断开连接
                        # 清空缓冲区
                        if serial_port.in_waiting > 0:
                            serial_port.read(serial_port.in_waiting)
                    except Exception as e:
                        print(f"⚠️ 断开连接时出错: {e}")
                
                # 递归调用重试整个流程
                return self.send_vbat_mqtt_alert(vbat_mv, alert_type, controller, emergency_mode, custom_data, max_retries - 1)
            
            # 4. 断开连接（可选步骤，不影响整体成功状态）
            if disconnect_template and disconnect_template.strip():
                print("🔗 断开连接...")
                print(f"   指令: {disconnect_cmd.strip()}")
                serial_port.write(disconnect_cmd.encode('utf-8'))
                
                # 等待断开连接响应
                disconnect_response = ""
                start_time = time.time()
                
                # 等待最多2秒获取响应
                while time.time() - start_time < 2:
                    if serial_port.in_waiting > 0:
                        chunk = serial_port.read(serial_port.in_waiting).decode('utf-8', errors='ignore')
                        disconnect_response += chunk
                        if 'OK' in disconnect_response.upper() or 'ERROR' in disconnect_response.upper():
                            break
                    time.sleep(0.1)
                
                if disconnect_response.strip():
                    print(f"   ← 响应: {disconnect_response.strip()}")
                    if self._check_command_success(disconnect_response, disconnect_cmd):
                        expected_pattern = self._get_expected_response_pattern(disconnect_cmd)
                        has_expected_pattern = expected_pattern in disconnect_response.upper()
                        
                        if has_expected_pattern and expected_pattern != 'OK':
                            pattern_text = f" ({expected_pattern}确认)"
                            print(f"   ✅ 断开连接成功{pattern_text}")
                        else:
                            print(f"   ✅ 断开连接成功")
                    else:
                        print(f"   ⚠️ 断开连接失败（非关键步骤）")
                else:
                    print(f"   ⚠️ 断开连接无响应（非关键步骤）")
            
            # 验证是否所有关键步骤都成功
            expected_steps = 4  # 连接、认证、订阅、发布
            if success_count >= expected_steps:
                print("✅ 数据发送完成")
                return True
            else:
                print(f"❌ 数据发送失败，仅完成 {success_count}/{expected_steps} 个关键步骤")
                return False
            
        except Exception as e:
            print(f"❌ MQTT发送失败: {e}")
            
            # 智能错误分析和建议
            error_type = "未知错误"
            suggestions = []
            
            if "ERROR" in str(e).upper():
                error_type = "指令执行错误"
                suggestions.extend([
                    "检查设备是否支持MQTT功能",
                    "确认AT指令格式是否正确",
                    "验证网络连接状态"
                ])
            elif "TIMEOUT" in str(e).upper() or "超时" in str(e):
                error_type = "连接超时"
                suggestions.extend([
                    "检查网络信号强度",
                    "增加指令等待时间",
                    "验证服务器地址和端口"
                ])
            else:
                error_type = "网络/配置错误"
                suggestions.extend([
                    "检查网络连接和MQTT服务器状态",
                    "验证MQTT服务器配置参数",
                    "确认设备网络功能已启用"
                ])
            
            print(f"🔍 错误类型: {error_type}")
            for i, suggestion in enumerate(suggestions, 1):
                print(f"   {i}. {suggestion}")
            
            return False  # 只有在异常时才返回False
            
            # 检查网络连接状态
            try:
                print("\n🌐 检查网络连接状态...")
                cereg_cmd = "AT+CEREG?\r\n"
                serial_port.write(cereg_cmd.encode('utf-8'))
                time.sleep(1)
                
                if serial_port.in_waiting > 0:
                    response = serial_port.read(serial_port.in_waiting).decode('utf-8', errors='ignore')
                    
                    # 解析CEREG响应并保存状态
                    if "+CEREG:" in response:
                        lines = response.split('\n')
                        for line in lines:
                            line = line.strip()
                            if line.startswith('+CEREG:'):
                                try:
                                    # 解析格式: +CEREG: <n>,<stat>[,<tac>,<ci>,<AcT>]
                                    parts = line.split(':')[1].split(',')
                                    if len(parts) >= 2:
                                        stat = int(parts[1].strip())
                                        status_map = {
                                            0: "未注册，未搜索网络",
                                            1: "已注册，本地网络",
                                            2: "未注册，正在搜索网络",
                                            3: "注册被拒绝",
                                            4: "未知状态",
                                            5: "已注册，漫游网络"
                                        }
                                        status_text = status_map.get(stat, f"未知状态({stat})")
                                        
                                        # 保存网络状态
                                        self._last_network_status = status_text
                                        
                                        if stat in [1, 5]:
                                            print(f"   ✅ 网络状态: {status_text}")
                                        else:
                                            print(f"   ❌ 网络状态: {status_text}")
                                            
                                except (IndexError, ValueError):
                                    error_text = f"网络状态解析失败: {line}"
                                    self._last_network_status = error_text
                                    print(f"   ⚠️ {error_text}")
                    else:
                        no_response_text = "无法获取网络注册状态"
                        self._last_network_status = no_response_text
                        print(f"   ⚠️ {no_response_text}")
                        
            except Exception as cereg_error:
                print(f"⚠️ 网络连接检查失败: {cereg_error}")
            
            # 发送AT+CLAC查询可用指令，保存并分类MT/MQTT相关指令
            try:
                print("\n🔍 正在检测设备支持的指令...")
                clac_cmd = "AT+CLAC\r\n"
                serial_port.write(clac_cmd.encode('utf-8'))
                
                # 等待完整响应直到收到OK或ERROR
                response = ""
                start_time = time.time()
                command_complete = False
                
                print("⏳ 等待设备响应...")
                while time.time() - start_time < 15:  # 最多等待15秒
                    if serial_port.in_waiting > 0:
                        chunk = serial_port.read(serial_port.in_waiting).decode('utf-8', errors='ignore')
                        response += chunk
                        
                        # 检查是否收到完整的响应结束标志
                        if 'OK\r\n' in response or 'ERROR\r\n' in response or 'OK\n' in response or 'ERROR\n' in response:
                            command_complete = True
                            print("✅ 收到完整响应，开始解析...")
                            break
                    
                    # 每2秒显示一次等待状态
                    elapsed = int(time.time() - start_time)
                    if elapsed % 2 == 0 and elapsed > 0:
                        print(f"   等待中... {elapsed}s")
                    
                    time.sleep(0.1)
                
                if not command_complete:
                    print("⚠️ 等待响应超时，使用已接收数据继续解析")
                
                print(f"📥 原始响应长度: {len(response)} 字符")
                if response:
                    print(f"📄 原始响应内容预览:\n{repr(response[:500])}")
                    if len(response) > 500:
                        print(f"   ... (共{len(response)}字符)")
                    
                    # 重置缓存
                    self._all_detected_commands.clear()
                    self._detected_mt_commands.clear()
                    self._detected_mqtt_commands.clear()
                    
                    # 提取并分类所有指令 - 更智能的解析
                    lines = response.replace('\r\n', '\n').replace('\r', '\n').split('\n')
                    
                    for line in lines:
                        line = line.strip()
                        # 更精确的过滤：只保留有效的AT指令，排除所有响应状态
                        if line and not line.upper().startswith(('OK', 'ERROR', '+', 'AT+CLAC')):
                            # 清理和标准化指令格式
                            cmd = line.strip()
                            
                            # 确保是有效的AT指令
                            if cmd and (cmd.upper().startswith('AT+') or cmd.upper().startswith('AT')):
                                # 标准化格式
                                if not cmd.upper().startswith('AT'):
                                    cmd = 'AT' + cmd
                                
                                # 去重并添加
                                if cmd not in self._all_detected_commands:
                                    self._all_detected_commands.append(cmd)
                                    
                                    # 分类保存 - 排除CTM2M指令，只保留标准MT和MQTT指令
                            cmd_upper = cmd.upper()
                            
                            # 跳过CTM2M指令
                            if 'CTM2M' in cmd_upper:
                                continue  # 忽略CTM2M指令
                            elif any(keyword in cmd_upper for keyword in ['ECMT', 'MQTT', 'MQT']):
                                self._detected_mqtt_commands.append(cmd)
                            elif 'MT' in cmd_upper and 'CTM2M' not in cmd_upper:
                                self._detected_mt_commands.append(cmd)
                    
                    # 显示结果并保存到文件
                    if self._detected_mqtt_commands or self._detected_mt_commands:
                        if self._detected_mqtt_commands:
                            print("📋 发现的MQTT相关指令:")
                            for cmd in self._detected_mqtt_commands:
                                print(f"   • {cmd}")
                        
                        if self._detected_mt_commands:
                            print("📋 发现的MT相关指令:")
                            for cmd in self._detected_mt_commands:
                                print(f"   • {cmd}")
                        
                        # 保存指令到文件
                        self._save_device_commands_to_file()
                        print(f"💾 已保存{len(self._all_detected_commands)}条指令到文件")
                    else:
                        print("⚠️ 未检测到MT/MQTT相关指令，设备可能不支持MQTT功能")
                        print("   建议检查设备手册或联系厂商确认MQTT支持情况")
                        
            except Exception as clac_error:
                print(f"⚠️ 无法查询设备指令: {clac_error}")
    
    def _get_network_status(self):
        """获取网络状态信息"""
        try:
            # 返回实际检测到的最新网络状态
            if self._last_network_status:
                return self._last_network_status
            else:
                return "网络状态未检测（尚未进行网络检查）"
        except:
            return "网络状态获取失败"
    
    def detect_device_commands(self, controller):
        """主动检测设备支持的指令"""
        try:
            # 从控制器获取serial_port
            serial_port = None
            if hasattr(controller, 'serial_controller'):
                serial_controller = controller.serial_controller
                if hasattr(serial_controller, 'serial_port'):
                    serial_port = serial_controller.serial_port
            
            if not serial_port or not serial_port.is_open:
                print("❌ 端口未连接，无法检测设备指令")
                return False
                
            print("🔍 正在检测设备支持的指令...")
            
            # 发送AT+CLAC查询可用指令
            clac_cmd = "AT+CLAC\r\n"
            serial_port.write(clac_cmd.encode('utf-8'))
            
            # 等待完整响应直到收到OK或ERROR
            response = ""
            start_time = time.time()
            command_complete = False
            
            print("⏳ 等待设备响应...")
            while time.time() - start_time < 15:  # 最多等待15秒
                if serial_port.in_waiting > 0:
                    chunk = serial_port.read(serial_port.in_waiting).decode('utf-8', errors='ignore')
                    response += chunk
                    
                    # 检查是否收到完整的响应结束标志
                    if 'OK\r\n' in response or 'ERROR\r\n' in response or 'OK\n' in response or 'ERROR\n' in response:
                        command_complete = True
                        print("✅ 收到完整响应，开始解析...")
                        break
                
                # 每2秒显示一次等待状态
                elapsed = int(time.time() - start_time)
                if elapsed % 2 == 0 and elapsed > 0:
                    print(f"   等待中... {elapsed}s")
                
                time.sleep(0.1)
            
            if not command_complete:
                print("⚠️ 等待响应超时，使用已接收数据继续解析")
            
            print(f"📥 原始响应长度: {len(response)} 字符")
            if response:
                print(f"📄 原始响应内容预览:\n{repr(response[:500])}")
                if len(response) > 500:
                    print(f"   ... (共{len(response)}字符)")
                
                # 重置缓存
                self._all_detected_commands.clear()
                self._detected_mt_commands.clear()
                self._detected_mqtt_commands.clear()
                
                # 提取并分类所有指令 - 更智能的解析
                lines = response.replace('\r\n', '\n').replace('\r', '\n').split('\n')
                
                for line in lines:
                    line = line.strip()
                    # 更精确的过滤：只保留有效的AT指令，排除所有响应状态
                    if line and not line.upper().startswith(('OK', 'ERROR', '+', 'AT+CLAC')):
                        # 清理和标准化指令格式
                        cmd = line.strip()
                        
                        # 确保是有效的AT指令
                        if cmd and (cmd.upper().startswith('AT+') or cmd.upper().startswith('AT')):
                            # 标准化格式
                            if not cmd.upper().startswith('AT'):
                                cmd = 'AT' + cmd
                            
                            # 去重并添加
                            if cmd not in self._all_detected_commands:
                                self._all_detected_commands.append(cmd)
                                
                                # 分类保存 - 排除CTM2M指令，只保留标准MT和MQTT指令
                            cmd_upper = cmd.upper()
                            
                            # 跳过CTM2M指令
                            if 'CTM2M' in cmd_upper:
                                continue  # 忽略CTM2M指令
                            elif any(keyword in cmd_upper for keyword in ['MQTT', 'MQT']):
                                self._detected_mqtt_commands.append(cmd)
                            elif 'ECMT' in cmd_upper:
                                self._detected_mqtt_commands.append(cmd)
                            elif 'MT' in cmd_upper and 'CTM2M' not in cmd_upper:
                                self._detected_mt_commands.append(cmd)
                
                # 显示结果并保存到文件
                print(f"\n📊 检测结果:")
                print(f"   总指令数: {len(self._all_detected_commands)}")
                print(f"   MQTT指令: {len(self._detected_mqtt_commands)}")
                print(f"   MT指令: {len(self._detected_mt_commands)}")
                
                if self._detected_mqtt_commands:
                    print("\n📋 MQTT相关指令:")
                    for cmd in self._detected_mqtt_commands:
                        print(f"   • {cmd}")
                
                if self._detected_mt_commands:
                    print("\n📋 MT相关指令:")
                    for cmd in self._detected_mt_commands:
                        print(f"   • {cmd}")
                
                if self._all_detected_commands and not (self._detected_mqtt_commands or self._detected_mt_commands):
                    print("\n📋 其他指令:")
                    for cmd in self._all_detected_commands[:10]:  # 只显示前10条
                        print(f"   • {cmd}")
                    if len(self._all_detected_commands) > 10:
                        print(f"   ... 还有 {len(self._all_detected_commands) - 10} 条指令")
                
                # 保存指令到文件
                self._save_device_commands_to_file()
                print(f"\n💾 已保存{len(self._all_detected_commands)}条指令到文件")
                
                return True
            else:
                # 获取当前连接信息用于诊断
                port_info = "未知端口"
                baud_info = "未知波特率"
                if hasattr(self, 'serial_controller') and self.serial_controller.serial_port:
                    port = self.serial_controller.serial_port
                    if port and port.is_open:
                        port_info = port.name
                        baud_info = str(port.baudrate)
                
                print(f"\n⚠️ 设备无响应或AT+CLAC指令不被支持")
                print(f"📡 当前连接: {port_info}@{baud_info}")
                print(f"🔍 可能原因:")
                print(f"   • 波特率不匹配 (当前: {baud_info})")
                print(f"   • 模组未开机或已关机")
                print(f"   • 模组不支持AT+CLAC指令")
                print(f"   • 串口线连接问题")
                print(f"   • 模组处于异常状态")
                print(f"\n💡 建议操作:")
                print(f"   1. 检查模组是否开机")
                print(f"   2. 确认波特率设置 (常用: 9600, 115200)")
                print(f"   3. 重新插拔串口线")
                print(f"   4. 重启模组")
                print(f"   5. 尝试其他AT指令测试 (如: AT)")
                return False
                
        except Exception as e:
            print(f"⚠️ 检测设备指令失败: {e}")
            return False

    def _get_mqtt_config_info(self):
        """获取MQTT配置信息和建议，与设备指令进行比对"""
        try:
            config_info = {
                'summary': '',
                'missing': [],
                'format': {},
                'suggestions': '',
                'device_commands': {
                    'mt': self._detected_mt_commands,
                    'mqtt': self._detected_mqtt_commands,
                    'all': self._all_detected_commands
                }
            }
            
            # 检查必需配置项 - 包含完整的ECMT指令集
            required_configs = [
                'vbat_mqtt_broker', 'vbat_mqtt_port', 'vbat_mqtt_topic',
                'vbat_mqtt_connect_cmd', 'vbat_mqtt_auth_cmd', 
                'vbat_mqtt_subscribe_cmd', 'vbat_mqtt_publish_cmd'
            ]
            
            missing = []
            current_config = {}
            
            # 获取当前配置值
            for config_key in required_configs:
                value = getattr(self, config_key, None)
                if not value:
                    missing.append(config_key.replace('vbat_mqtt_', ''))
                else:
                    current_config[config_key.replace('vbat_mqtt_', '')] = str(value)
            
            # 检查指令格式是否与设备匹配
            connect_cmd = getattr(self, 'vbat_mqtt_connect_cmd', '')
            publish_cmd = getattr(self, 'vbat_mqtt_publish_cmd', '')
            
            # 分析指令类型
            cmd_analysis = []
            if 'ECMT' in connect_cmd.upper():
                cmd_analysis.append("使用ECMT指令集")
            elif 'MQTT' in connect_cmd.upper():
                cmd_analysis.append("使用标准MQTT指令集")
            elif 'MT' in connect_cmd.upper():
                cmd_analysis.append("使用MT指令集")
            else:
                cmd_analysis.append("未知指令格式")
            
            # 检查设备指令匹配情况
            device_match = []
            if self._detected_mqtt_commands:
                device_match.append(f"发现{len(self._detected_mqtt_commands)}条MQTT指令")
            if self._detected_mt_commands:
                device_match.append(f"发现{len(self._detected_mt_commands)}条MT指令")
            
            if missing:
                config_info['summary'] = f"配置不完整 ({len(missing)}项缺失)"
                config_info['missing'] = missing
                if device_match:
                    config_info['suggestions'] = f"请检查config/vbat_config.json中的MQTT配置，设备支持: {', '.join(device_match)}"
                else:
                    config_info['suggestions'] = "请检查config/vbat_config.json中的MQTT配置"
            else:
                config_info['summary'] = f"配置完整 ({', '.join(cmd_analysis)})"
                if device_match:
                    config_info['suggestions'] = f"请确认指令格式与设备支持的{', '.join(device_match)}匹配"
                else:
                    config_info['suggestions'] = "请确认指令格式与设备支持的AT指令匹配"
            
            # 格式化显示当前配置（表格格式）
            config_info['format'] = {
                '配置项': '当前值',
                '服务器': f"{getattr(self, 'vbat_mqtt_broker', '未设置')}:{getattr(self, 'vbat_mqtt_port', '未设置')}",
                '主题': getattr(self, 'vbat_mqtt_topic', '未设置'),
                '连接指令': self._extract_command_base(getattr(self, 'vbat_mqtt_connect_cmd', '未设置')),
                '认证指令': self._extract_command_base(getattr(self, 'vbat_mqtt_auth_cmd', '未设置')),
                '订阅指令': self._extract_command_base(getattr(self, 'vbat_mqtt_subscribe_cmd', '未设置')),
                '发布指令': self._extract_command_base(getattr(self, 'vbat_mqtt_publish_cmd', '未设置')),
                '断开指令': self._extract_command_base(getattr(self, 'vbat_mqtt_disconnect_cmd', '未设置'))
            }
            
            # 添加指令匹配检查结果
            config_info['command_match'] = self._check_command_compatibility()
            
            # 添加设备指令列表（用于对比）
            if self._detected_mqtt_commands or self._detected_mt_commands:
                config_info['device_commands'] = {
                    'MQTT指令': self._detected_mqtt_commands,
                    'MT指令': self._detected_mt_commands
                }
            
            return config_info
            
        except Exception as e:
            return {
                'summary': f'配置检查失败: {e}',
                'missing': [],
                'format': {'error': str(e)},
                'suggestions': '请检查配置文件格式和路径'
            }
    
    def _extract_command_base(self, command_str):
        """提取指令的基础部分（去除参数）"""
        if not command_str or command_str == '未设置':
            return '未设置'
        
        # 提取AT指令的基础部分
        match = re.search(r'(AT\+[A-Z]+)', str(command_str).upper())
        if match:
            return match.group(1)
        
        # 尝试匹配其他可能的格式
        match = re.search(r'(AT\+[A-Z0-9]+)', str(command_str).upper())
        if match:
            return match.group(1)
        
        # 如果没有匹配到标准格式，返回前几个字符
        return str(command_str)[:15] + '...' if len(str(command_str)) > 15 else str(command_str)
    
    def _save_device_commands_to_file(self):
        """将设备支持的指令保存到文件"""
        try:
            import json
            import os
            from datetime import datetime
            
            # 创建数据目录
            data_dir = os.path.join(os.path.dirname(__file__), '..', '..', 'data')
            os.makedirs(data_dir, exist_ok=True)
            
            # 生成文件名
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            filename = f"device_commands_{timestamp}.json"
            filepath = os.path.join(data_dir, filename)
            
            # 准备保存的数据
            device_data = {
                "timestamp": datetime.now().isoformat(),
                "total_commands": len(self._all_detected_commands),
                "mqtt_commands": self._detected_mqtt_commands,
                "mt_commands": self._detected_mt_commands,
                "all_commands": self._all_detected_commands,
                "mqtt_count": len(self._detected_mqtt_commands),
                "mt_count": len(self._detected_mt_commands)
            }
            
            # 只保存最新版本（覆盖旧文件）
            latest_path = os.path.join(data_dir, "device_commands_latest.json")
            with open(latest_path, 'w', encoding='utf-8') as f:
                json.dump(device_data, f, ensure_ascii=False, indent=2)
                
            # 自动清理历史文件（保留最新版本）
            self._cleanup_old_command_files(data_dir)
                
            print(f"   🔄 设备指令已更新到: device_commands_latest.json")
            
        except Exception as e:
            print(f"⚠️ 保存指令到文件失败: {e}")
    
    def _cleanup_old_command_files(self, data_dir):
        """清理旧的设备指令文件，只保留最新版本"""
        try:
            import glob
            
            # 查找所有历史文件（不包括latest.json）
            pattern = os.path.join(data_dir, "device_commands_*.json")
            old_files = glob.glob(pattern)
            
            # 排除最新版本文件
            latest_file = os.path.join(data_dir, "device_commands_latest.json")
            old_files = [f for f in old_files if f != latest_file]
            
            # 删除旧文件
            for old_file in old_files:
                try:
                    os.remove(old_file)
                except (OSError, IOError):
                    pass  # 忽略删除错误
                    
        except Exception:
            pass  # 忽略清理错误
    
    def load_device_commands_from_file(self):
        """从文件加载设备支持的指令"""
        try:
            import json
            import os
            
            data_dir = os.path.join(os.path.dirname(__file__), '..', '..', 'data')
            latest_path = os.path.join(data_dir, "device_commands_latest.json")
            
            if not os.path.exists(latest_path):
                print("⚠️ 未找到设备指令文件，请先运行AT+CLAC检测")
                return None
            
            with open(latest_path, 'r', encoding='utf-8') as f:
                device_data = json.load(f)
            
            # 更新缓存
            self._all_detected_commands = device_data.get('all_commands', [])
            self._detected_mqtt_commands = device_data.get('mqtt_commands', [])
            self._detected_mt_commands = device_data.get('mt_commands', [])
            
            print(f"✅ 已加载设备指令数据:")
            print(f"   • 总指令数: {len(self._all_detected_commands)}")
            print(f"   • MQTT指令: {len(self._detected_mqtt_commands)}")
            print(f"   • MT指令: {len(self._detected_mt_commands)}")
            
            return device_data
            
        except Exception as e:
            print(f"⚠️ 加载设备指令文件失败: {e}")
            return None
    
    def _check_command_compatibility(self):
        """检查配置指令与设备指令的兼容性"""
        try:
            # 获取配置中的基础指令 - 包含完整的ECMT指令集
            connect_cmd = getattr(self, 'vbat_mqtt_connect_cmd', '')
            auth_cmd = getattr(self, 'vbat_mqtt_auth_cmd', '')
            subscribe_cmd = getattr(self, 'vbat_mqtt_subscribe_cmd', '')
            publish_cmd = getattr(self, 'vbat_mqtt_publish_cmd', '')
            
            config_commands = []
            if connect_cmd:
                config_commands.append(self._extract_command_base(connect_cmd))
            if auth_cmd:
                config_commands.append(self._extract_command_base(auth_cmd))
            if subscribe_cmd:
                config_commands.append(self._extract_command_base(subscribe_cmd))
            if publish_cmd:
                config_commands.append(self._extract_command_base(publish_cmd))
            
            # 获取设备支持的基础指令
            device_commands = []
            for cmd in self._detected_mqtt_commands + self._detected_mt_commands:
                device_commands.append(self._extract_command_base(cmd))
            
            # 检查匹配情况
            match_result = {
                'status': '未知',
                'matches': [],
                'mismatches': [],
                'device_support': device_commands,
                'config_commands': config_commands
            }
            
            if not device_commands:
                # 尝试从文件加载已保存的设备指令
                print("🔍 未检测到设备指令，尝试从文件加载...")
                loaded_commands = self.load_device_commands_from_file()
                
                if loaded_commands and (loaded_commands.get('mqtt_commands') or loaded_commands.get('mt_commands')):
                    # 使用已保存的指令
                    mqtt_commands = loaded_commands.get('mqtt_commands', [])
                    mt_commands = loaded_commands.get('mt_commands', [])
                    device_commands = mqtt_commands + mt_commands
                    print(f"✅ 从文件加载成功: MQTT({len(mqtt_commands)}) MT({len(mt_commands)})")
                else:
                    # 没有保存的指令，立即进行检测
                    print("🔍 无保存的指令，正在检测设备指令...")
                    try:
                        # 执行指令检测 - 使用detect_device_commands方法
                        # 这个方法会在有连接时自动检测指令
                        print("⚠️ 设备未连接或指令未检测，请选择菜单中的'检测设备指令'选项")
                        device_commands = []
                    except Exception as e:
                        print(f"⚠️ 指令检测失败: {e}")
                        device_commands = []
                
                if not device_commands:
                    match_result['status'] = '无法检测设备指令'
                    match_result['suggestions'] = '请确保设备已连接，然后选择"检测设备指令"选项'
                    return match_result
            
            if not config_commands:
                match_result['status'] = '配置不完整'
                match_result['suggestions'] = '请配置MQTT连接和发布指令'
                return match_result
            
            # 检查匹配
            matches = []
            mismatches = []
            
            for config_cmd in config_commands:
                if config_cmd in device_commands:
                    matches.append(config_cmd)
                else:
                    mismatches.append(config_cmd)
            
            if mismatches:
                match_result['status'] = '指令不匹配'
                match_result['matches'] = matches
                match_result['mismatches'] = mismatches
                match_result['suggestions'] = f'请检查设备是否支持: {", ".join(mismatches)}'
            else:
                match_result['status'] = '指令匹配'
                match_result['matches'] = matches
                match_result['suggestions'] = '配置指令与设备支持匹配'
            
            return match_result
            
        except Exception as e:
            return {
                'status': '检查失败',
                'error': str(e),
                'suggestions': '请重新检测设备指令'
            }
    
    def view_vbat_records(self):
        """查看VBAT记录"""
        if not self.vbat_records:
            print("⚠️ 没有VBAT记录")
            return
        
        print("\n🔋 VBAT记录")
        print("=" * 40)
        
        # 显示统计信息
        records = self.vbat_records
        if records:
            vbat_values = [r["vbat"] for r in records]
            avg_vbat = sum(vbat_values) / len(vbat_values)
            min_vbat = min(vbat_values)
            max_vbat = max(vbat_values)
            
            print(f"📊 统计信息:")
            print(f"   记录数: {len(records)}")
            print(f"   平均值: {avg_vbat:.0f}mV")
            print(f"   最小值: {min_vbat}mV")
            print(f"   最大值: {max_vbat}mV")
            print(f"   低阈值: {self.vbat_threshold_low}mV")
            print(f"   高阈值: {self.vbat_threshold_high}mV")
            print()
        
        # 显示最近20条记录
        recent_records = records[-20:]
        print("📋 最近记录:")
        for record in recent_records:
            timestamp = record["timestamp"][:19]
            vbat = record["vbat"]
            status = ""
            if vbat <= self.vbat_threshold_low:
                status = " ⚠️低"
            elif vbat >= self.vbat_threshold_high:
                status = " ⚠️高"
            print(f"   {timestamp}: {vbat}mV{status}")
        
        input("\n按回车键返回...")
    
    def export_vbat_data(self):
        """导出VBAT数据"""
        if not self.vbat_records:
            print("⚠️ 没有VBAT数据可导出")
            return
        
        try:
            filename = f"vbat_data_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
            with open(filename, 'w', newline='', encoding='utf-8-sig') as f:
                writer = csv.DictWriter(f, fieldnames=["timestamp", "vbat"])
                writer.writeheader()
                writer.writerows(self.vbat_records)
            
            print(f"✅ VBAT数据已导出到: {filename}")
            
        except Exception as e:
            print(f"❌ 导出失败: {e}")
    
    def save_named_config(self, named_config):
        """保存命名配置"""
        try:
            # 读取现有命名配置
            named_configs = []
            try:
                with open('mqtt_named_configs.json', 'r', encoding='utf-8') as f:
                    named_configs = json.load(f)
            except (FileNotFoundError, json.JSONDecodeError):
                named_configs = []
            
            # 检查是否已存在同名配置
            existing_names = [c["name"] for c in named_configs]
            if named_config["name"] in existing_names:
                # 如果是交互式保存，询问用户是否更新
                if hasattr(self, 'interactive_save') and self.interactive_save:
                    confirm = input(f"配置 '{named_config['name']}' 已存在，是否更新替换? (y/n): ").strip().lower()
                    if confirm != 'y':
                        print("❌ 保存已取消")
                        return False
                # 更新现有配置
                for i, config in enumerate(named_configs):
                    if config["name"] == named_config["name"]:
                        named_configs[i] = named_config
                        break
            else:
                # 添加新配置
                named_configs.append(named_config)
            
            # 保存到文件
            with open('mqtt_named_configs.json', 'w', encoding='utf-8') as f:
                json.dump(named_configs, f, indent=2, ensure_ascii=False)
                
            return True
        except Exception as e:
            print(f"保存命名配置失败: {e}")
            return False
    
    def load_named_configs(self):
        """加载所有命名配置"""
        try:
            with open('mqtt_named_configs.json', 'r', encoding='utf-8') as f:
                return json.load(f)
        except (FileNotFoundError, json.JSONDecodeError):
            return []
    
    def _internal_detect_device_commands(self):
        """内部方法：使用现有serial_controller检测设备指令"""
        try:
            # 获取当前serial_port
            serial_port = None
            if hasattr(self, 'serial_controller') and self.serial_controller.serial_port:
                serial_port = self.serial_controller.serial_port
            
            if not serial_port or not serial_port.is_open:
                print("   ❌ 端口未连接，无法检测设备指令")
                return False
                
            print("   📡 正在检测设备支持的指令...")
            
            # 发送AT+CLAC查询可用指令
            clac_cmd = "AT+CLAC\r\n"
            serial_port.write(clac_cmd.encode('utf-8'))
            
            # 等待完整响应
            response = ""
            start_time = time.time()
            command_complete = False
            
            while time.time() - start_time < 10:  # 最多等待10秒
                if serial_port.in_waiting > 0:
                    chunk = serial_port.read(serial_port.in_waiting).decode('utf-8', errors='ignore')
                    response += chunk
                    
                    # 检查是否收到完整的响应
                    if 'OK\r\n' in response or 'ERROR\r\n' in response or 'OK\n' in response or 'ERROR\n' in response:
                        command_complete = True
                        break
                
                time.sleep(0.1)
            
            if not command_complete:
                print("   ⚠️ 等待响应超时")
                return False
            
            # 解析指令
            self._all_detected_commands.clear()
            self._detected_mt_commands.clear()
            self._detected_mqtt_commands.clear()
            
            lines = response.replace('\r\n', '\n').replace('\r', '\n').split('\n')
            
            for line in lines:
                line = line.strip()
                if line and not line.upper().startswith(('OK', 'ERROR', '+', 'AT+CLAC')):
                    cmd = line.strip()
                    if cmd and (cmd.upper().startswith('AT+') or cmd.upper().startswith('AT')):
                        if not cmd.upper().startswith('AT'):
                            cmd = 'AT' + cmd
                        
                        if cmd not in self._all_detected_commands:
                            self._all_detected_commands.append(cmd)
                            
                            cmd_upper = cmd.upper()
                            if 'ECMT' in cmd_upper:
                                self._detected_mqtt_commands.append(cmd)
                            elif 'MT' in cmd_upper and 'CTM2M' not in cmd_upper:
                                self._detected_mt_commands.append(cmd)
                            elif any(keyword in cmd_upper for keyword in ['MQTT', 'MQT']):
                                self._detected_mqtt_commands.append(cmd)
            
            # 保存到文件
            self._save_device_commands_to_file()
            
            print(f"   ✅ 检测到 {len(self._all_detected_commands)} 条指令")
            print(f"   📊 MQTT指令: {len(self._detected_mqtt_commands)}")
            print(f"   📊 MT指令: {len(self._detected_mt_commands)}")
            
            return True
            
        except Exception as e:
            print(f"   ⚠️ 指令检测失败: {e}")
            return False

    def apply_named_config(self, config_name):
        """应用指定名称的配置"""
        try:
            named_configs = self.load_named_configs()
            for config in named_configs:
                if config["name"] == config_name:
                    # 应用配置
                    self.vbat_mqtt_broker = config["server"]
                    self.vbat_mqtt_port = config["port"]
                    self.vbat_mqtt_topic = config["topic"]
                    self.vbat_mqtt_qos = config["qos"]
                    self.vbat_mqtt_connect_cmd = config["connect_cmd"]
                    if "auth_cmd" in config:
                        self.vbat_mqtt_auth_cmd = config["auth_cmd"]
                    self.vbat_mqtt_subscribe_cmd = config["subscribe_cmd"]
                    self.vbat_mqtt_publish_cmd = config["publish_cmd"]
                    if "disconnect_cmd" in config:
                        self.vbat_mqtt_disconnect_cmd = config["disconnect_cmd"]
                    self.save_vbat_config()
                    return True
            return False
        except Exception as e:
            print(f"应用命名配置失败: {e}")
            return False
    
    def delete_named_config(self, config_name):
        """删除指定名称的配置"""
        try:
            named_configs = self.load_named_configs()
            named_configs = [c for c in named_configs if c["name"] != config_name]
            
            with open('mqtt_named_configs.json', 'w', encoding='utf-8') as f:
                json.dump(named_configs, f, indent=2, ensure_ascii=False)
            return True
        except Exception as e:
            print(f"删除命名配置失败: {e}")
            return False
    
    def generate_mqtt_command(self, command_type, **kwargs):
        """根据模板和参数生成实际的MQTT指令"""
        templates = {
            'connect': self.vbat_mqtt_connect_template,
            'subscribe': self.vbat_mqtt_subscribe_template,
            'publish': self.vbat_mqtt_publish_template,
            'disconnect': self.vbat_mqtt_disconnect_template,
            'auth': self.vbat_mqtt_auth_template
        }
        
        template = templates.get(command_type)
        if not template:
            return None
        
        # 构建参数字典
        params = {
            'host_name': self.vbat_mqtt_broker or 'localhost',
            'port': self.vbat_mqtt_port or 1883,
            'topic': self.vbat_mqtt_topic or '/test',
            'qos': self.vbat_mqtt_qos or 0,
            'client_id': self.vbat_mqtt_client_id or 'client_001',
            'username': self.vbat_mqtt_username or '',
            'password': self.vbat_mqtt_password or '',
            'payload': kwargs.get('payload', '{"vbat":{vbat}}')
        }
        
        # 更新用户提供的参数
        params.update(kwargs)
        
        try:
            return template.format(**params)
        except KeyError as e:
            print(f"模板参数错误: {e}")
            return None

    def _get_expected_response_pattern(self, command):
        """根据指令自动生成期望的响应模式"""
        command_upper = command.upper().strip()
        
        # 指令到响应模式的映射
        response_patterns = {
            'ECMTOPEN': '+ECMTOPEN',
            'ECMTCONN': '+ECMTCONN', 
            'ECMTSUB': '+ECMTSUB',
            'ECMTPUB': '+ECMTPUB',
            'ECMTDISC': '+ECMTDISC',
            'ECMTCLOSE': '+ECMTCLOSE'
        }
        
        # 根据指令内容匹配响应模式
        for key, pattern in response_patterns.items():
            if key in command_upper:
                return pattern
        
        # 默认返回OK
        return 'OK'

    def _check_command_success(self, response, command):
        """根据指令类型检查响应是否成功"""
        if not response or not command:
            return False
            
        response_upper = response.upper()
        command_upper = command.upper()
        
        # 获取期望的响应模式
        expected_pattern = self._get_expected_response_pattern(command)
        
        # 检查基本成功条件：只要有OK且无ERROR就算成功
        has_ok = 'OK' in response_upper and 'ERROR' not in response_upper
        
        # 特定确认返回是加分项，但不是必须项
        has_expected_pattern = expected_pattern in response_upper
        
        # 主要判断：OK且无ERROR即可
        # 特定模式存在时提供额外确认信息，但不影响成功判定
        return has_ok
    
    def show_named_configs(self):
        """显示所有命名配置"""
        try:
            named_configs = self.load_named_configs()
            if not named_configs:
                print("ℹ️  暂无保存的命名配置")
                return False
            
            print("\n📋 已保存的命名配置")
            print("=" * 50)
            for i, config in enumerate(named_configs, 1):
                print(f"{i}. {config['name']}")
                print(f"   服务器: {config['server']}:{config['port']}")
                print(f"   主题: {config['topic']}")
                print(f"   QoS: {config['qos']}")
                print(f"   🔗 连接: {config.get('connect_cmd', '未设置')}")
                print(f"   🔐 认证: {config.get('auth_cmd', '未设置')}")
                print(f"   📋 订阅: {config.get('subscribe_cmd', '未设置')}")
                print(f"   📤 发布: {config.get('publish_cmd', '未设置')}")
                print(f"   🚫 断开: {config.get('disconnect_cmd', '未设置')}")
                print(f"   创建时间: {config['created_at']}")
                print()
            return True
        except Exception as e:
            print(f"显示命名配置失败: {e}")
            return False
    
    def manage_named_configs(self):
        """管理命名配置"""
        while True:
            # 注意：clear_screen函数需要在主控制器中定义
            # clear_screen()
            print("🏷️ 管理命名配置")
            print("=" * 30)
            
            # 显示现有配置
            has_configs = self.show_named_configs()
            
            print("\n操作选项:")
            if has_configs:
                print("1. 使用配置")
                print("2. 删除配置")
                print("3. 返回")
            else:
                print("3. 返回")
            
            choice = input("请选择操作 (1-3): ").strip()
            
            if choice == "1" and has_configs:
                try:
                    config_index = int(input("请输入配置序号: ").strip()) - 1
                    named_configs = self.load_named_configs()
                    if 0 <= config_index < len(named_configs):
                        config_name = named_configs[config_index]["name"]
                        if self.apply_named_config(config_name):
                            print(f"✅ 已应用配置: {config_name}")
                        else:
                            print("❌ 应用配置失败")
                    else:
                        print("❌ 序号无效")
                except ValueError:
                    print("❌ 请输入有效数字")
            elif choice == "2" and has_configs:
                try:
                    config_index = int(input("请输入要删除的配置序号: ").strip()) - 1
                    named_configs = self.load_named_configs()
                    if 0 <= config_index < len(named_configs):
                        config_name = named_configs[config_index]["name"]
                        confirm = input(f"确认删除配置 '{config_name}'? (y/n): ").strip().lower()
                        if confirm == 'y':
                            if self.delete_named_config(config_name):
                                print(f"✅ 已删除配置: {config_name}")
                            else:
                                print("❌ 删除配置失败")
                    else:
                        print("❌ 序号无效")
                except ValueError:
                    print("❌ 请输入有效数字")
            elif choice == "3":
                break
            else:
                if not has_configs:
                    break